/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gui;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import xmlproject.FileAndDirectorySizeUtils;
import xmlproject.XMLUtils;

/**
 *
 * @author Yayo
 */
public class MainWindow extends javax.swing.JFrame {

    String newLine = System.getProperty("line.separator");
    private String fileName = "";
    private String date = "";
    private AnalyzeSwingWorker analyzeSwingWorker;
    private CompareSwingWorker compareSwingWorker;

    /**
     * Creates new form MainWindow
     */
    public MainWindow() {
        initComponents();
        getCurrentDate();
        fillComboBox();
    }
    private PropertyChangeListener progressListener = new PropertyChangeListener() {
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals("progress")) {
                progressBar.setValue((Integer) evt.getNewValue());
            }
        }
    };

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        progressBar = new javax.swing.JProgressBar();
        statusLabel = new java.awt.Label();
        AnalyzeB = new java.awt.Button();
        CompareB = new java.awt.Button();
        QuitB = new java.awt.Button();
        ChooseDB = new java.awt.Button();
        jSeparator1 = new javax.swing.JSeparator();
        textField = new java.awt.TextField();
        ComboBoxXML = new javax.swing.JComboBox();
        jScrollPane1 = new javax.swing.JScrollPane();
        textArea = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("HDDAnalysis");
        setResizable(false);

        AnalyzeB.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
        AnalyzeB.setLabel("Analyze");
        AnalyzeB.setMinimumSize(new java.awt.Dimension(79, 27));
        AnalyzeB.setName(""); // NOI18N
        AnalyzeB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                AnalyzeBActionPerformed(evt);
            }
        });

        CompareB.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
        CompareB.setLabel("Compare");
        CompareB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                CompareBActionPerformed(evt);
            }
        });

        QuitB.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
        QuitB.setLabel("Quit");
        QuitB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                QuitBActionPerformed(evt);
            }
        });

        ChooseDB.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
        ChooseDB.setLabel("Choose Directory");
        ChooseDB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                ChooseDBActionPerformed(evt);
            }
        });

        textField.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N

        textArea.setColumns(20);
        textArea.setRows(5);
        jScrollPane1.setViewportView(textArea);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 1007, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(0, 0, Short.MAX_VALUE))
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(ComboBoxXML, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(jScrollPane1))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(CompareB, javax.swing.GroupLayout.DEFAULT_SIZE, 102, Short.MAX_VALUE)
                            .addComponent(QuitB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(ChooseDB, javax.swing.GroupLayout.PREFERRED_SIZE, 152, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(textField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(AnalyzeB, javax.swing.GroupLayout.PREFERRED_SIZE, 102, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(statusLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(AnalyzeB, javax.swing.GroupLayout.DEFAULT_SIZE, 37, Short.MAX_VALUE)
                    .addComponent(ChooseDB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(textField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(CompareB, javax.swing.GroupLayout.DEFAULT_SIZE, 40, Short.MAX_VALUE)
                    .addComponent(ComboBoxXML))
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(QuitB, javax.swing.GroupLayout.PREFERRED_SIZE, 38, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(16, 16, 16))
                    .addGroup(layout.createSequentialGroup()
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 420, Short.MAX_VALUE)
                        .addGap(18, 18, 18)))
                .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 7, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(14, 14, 14)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(statusLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 23, Short.MAX_VALUE))
                .addGap(6, 6, 6))
        );

        pack();
        setLocationRelativeTo(null);
    }// </editor-fold>//GEN-END:initComponents

    private void QuitBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_QuitBActionPerformed
        dispose();
    }//GEN-LAST:event_QuitBActionPerformed

    private void CompareBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_CompareBActionPerformed

        if (compareSwingWorker != null) {
            throw new IllegalStateException("Operation is already in progress");
        }
        textArea.setText("");
        textArea.setFocusable(false);
        CompareB.setEnabled(false);
        ChooseDB.setEnabled(false);
        QuitB.setEnabled(false);
        AnalyzeB.setEnabled(false);
        String selectedFile = "";
        String pathToAnalyze = "";
        if ((this.ComboBoxXML.getSelectedItem()) != null) {
            selectedFile = getFileName(this.ComboBoxXML.getSelectedItem().toString());
            if (FileAndDirectorySizeUtils.checkIfFileExists(selectedFile)) {
                File dir = new File(".");
                for (File file : dir.listFiles()) {
                    if (file.getName().equals(selectedFile)) {
                        try {
                            pathToAnalyze = XMLUtils.getPathFromXMLToAnalyze(file);
                        } catch (ParserConfigurationException ex) {
                            Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex);
                        } catch (SAXException ex) {
                            Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex);
                        } catch (IOException ex) {
                            Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
                this.compareSwingWorker = new CompareSwingWorker(selectedFile, pathToAnalyze);
                this.compareSwingWorker.addPropertyChangeListener(progressListener);
                this.compareSwingWorker.execute();
            } else {
                statusLabel.setText("File " + selectedFile + " does not exist!");
                this.ComboBoxXML.removeItemAt(this.ComboBoxXML.getSelectedIndex());
                AnalyzeB.setEnabled(true);
                ChooseDB.setEnabled(true);
                QuitB.setEnabled(true);
                CompareB.setEnabled(true);
            }
        } else {
            statusLabel.setText("No file selected from combobox!");
            AnalyzeB.setEnabled(true);
            ChooseDB.setEnabled(true);
            QuitB.setEnabled(true);
            CompareB.setEnabled(true);
        }
    }//GEN-LAST:event_CompareBActionPerformed

    private void AnalyzeBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_AnalyzeBActionPerformed

        if (analyzeSwingWorker != null) {
            throw new IllegalStateException("Operation is already in progress");
        }
        textArea.setText("");
        AnalyzeB.setEnabled(false);
        CompareB.setEnabled(false);
        ChooseDB.setEnabled(false);
        QuitB.setEnabled(false);
        String tmp = "";
        tmp = this.textField.getText();
        if (tmp.equals("")) {
            statusLabel.setText("You didn't select a directory to analyze !");
            AnalyzeB.setEnabled(true);
            ChooseDB.setEnabled(true);
            QuitB.setEnabled(true);
            CompareB.setEnabled(true);
            return;
        } else {
            this.analyzeSwingWorker = new AnalyzeSwingWorker(this.textField.getText(), this.date);
            this.analyzeSwingWorker.addPropertyChangeListener(progressListener);
            this.analyzeSwingWorker.execute();
        }
    }//GEN-LAST:event_AnalyzeBActionPerformed

    private void ChooseDBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ChooseDBActionPerformed

        CompareB.setEnabled(false);
        AnalyzeB.setEnabled(false);
        QuitB.setEnabled(false);
        textArea.setText("");
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.setCurrentDirectory(new java.io.File("."));
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        fileChooser.setAcceptAllFileFilterUsed(false);
        if (fileChooser.showDialog(CompareB, "OK") == JFileChooser.APPROVE_OPTION) {
            if (fileChooser.getSelectedFile().getName().equals("build")) {
                this.fileName = fileChooser.getSelectedFile().getName() + "2";
            } else {
                this.fileName = fileChooser.getSelectedFile().getName();       // nastavim premennej filename meno vybraneho adresara
            }
            this.textField.setText(fileChooser.getSelectedFile().getAbsolutePath());       //nastavim textbox s vybranym adresarom
        }
        AnalyzeB.setEnabled(true);
        ChooseDB.setEnabled(true);
        QuitB.setEnabled(true);
        CompareB.setEnabled(true);
    }//GEN-LAST:event_ChooseDBActionPerformed

    /**
     * Fills combobox with xml files
     */
    private void fillComboBox() {

        File dir = new File(".");
        if (dir.exists()) {
            for (File file : dir.listFiles()) {
                if (file.isFile() && XMLUtils.isXML(file)) {
                    if (!file.getName().equals("build.xml")) {
                        this.ComboBoxXML.addItem(this.date + "   " + file.getName());
                    }
                }
            }
        }
    }

    /**
     * Checks if item is already in combobox, if yes, then removes this item
     * from it
     *
     * @param fileName name of xml file to be look for in combobox
     */
    private void checkIfItemIsInCombobox(String fileName) {
        for (int i = 0; i < ComboBoxXML.getItemCount(); i++) {
            String comboBoxItem = getFileName(ComboBoxXML.getItemAt(i).toString());
            if (comboBoxItem.equals(fileName + ".xml")) {
                ComboBoxXML.removeItemAt(i);
            }
        }
    }

    /**
     * Gets current date
     */
    private void getCurrentDate() {
        DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");
        Date date = new Date();
        this.date = dateFormat.format(date);
    }

    private String getFileName(String comboBoxItem) {
        String fileName = "";

        int i = comboBoxItem.indexOf(" ");
        fileName = comboBoxItem.substring(i + 3);

        return fileName;
    }

    private class AnalyzeSwingWorker extends SwingWorker<String, Void> {

        private String pathToAnalyze;
        private String date;

        public AnalyzeSwingWorker(String pathToAnalyze, String date) {
            this.pathToAnalyze = pathToAnalyze;
            this.date = date;
        }

        @Override
        protected String doInBackground() throws Exception {
            statusLabel.setText("");
            int i = 0;
            // analyzing disk
            Document doc = null;
            File dir = new File(this.pathToAnalyze);

            doc = XMLUtils.createDoc();
            setProgress(i = i + 10);
            if (XMLUtils.checkIfDirecoryExist(dir)) {
                setProgress(i = i + 10);
                Element rootElement = XMLUtils.createRootElement(doc, this.pathToAnalyze, dir);
                setProgress(i = i + 10);
                XMLUtils.getFilesInDir(dir.listFiles(), rootElement, doc);
                setProgress(i = i + 60);
                XMLUtils.saveXML(doc, ".", fileName);
                setProgress(i = i + 10);


                return "Directory analyzed and XML file saved!";
            } else {
                setProgress(i = i + 90);
                return "";
            }
        }

        @Override
        protected void done() {
            try {
                AnalyzeB.setEnabled(true);
                ChooseDB.setEnabled(true);
                QuitB.setEnabled(true);
                CompareB.setEnabled(true);
                analyzeSwingWorker = null;
                if (get().equals("")) {
                    statusLabel.setText("Given path does not exist!");
                } else {
                    statusLabel.setText(get());
                    checkIfItemIsInCombobox(fileName);
                    ComboBoxXML.addItem(this.date + "   " + fileName + ".xml");    //ulozenie nazvu XML suboru do comboboxu

               }
                textField.setText("");

            } catch(ExecutionException ex) {
                JOptionPane.showMessageDialog(rootPane, "Exception thrown in doInBackground() of AnalyzeSwingWorker: " + ex.getCause() + newLine);
            } catch (InterruptedException ex) {
                throw new RuntimeException("Operation interrupted", ex);
            }
        }
    }

    private class CompareSwingWorker extends SwingWorker<String, String> {

        private String selectedFile;
        private String pathToAnalyze;
        private StringBuilder differencies = new StringBuilder();

        public CompareSwingWorker(String selectedFile, String pathToAnalyze) {
            this.selectedFile = selectedFile;
            this.pathToAnalyze = pathToAnalyze;
        }

        @Override
        protected String doInBackground() throws Exception {
            textArea.setText("");
            statusLabel.setText("");
            int i = 0;
            //analyzing disk
            Document doc = null;
            File dir = new File(pathToAnalyze);

            doc = XMLUtils.createDoc();
            setProgress(i = i + 3);
            if (XMLUtils.checkIfDirecoryExist(dir)) {
                setProgress(i = i + 3);
                Element rootElement = XMLUtils.createRootElement(doc, pathToAnalyze, dir);
                setProgress(i = i + 3);
                XMLUtils.getFilesInDir(dir.listFiles(), rootElement, doc);
                setProgress(i = i + 21);
                XMLUtils.saveXML(doc, ".", "temporary");
                setProgress(i = i + 3);
                publish("Directory analyzed and XML temporary file saved!");
            }

            //comparing 2 XML files
            FileReader fr1 = null;
            FileReader fr2 = null;
            fr1 = new FileReader(selectedFile);
            fr2 = new FileReader("temporary.xml");

            Diff diff = new Diff(fr1, fr2);
            if (diff.similar()) {
                differencies.append("Current state on hard drive in directory :" + newLine);
                differencies.append(pathToAnalyze);
                differencies.append(newLine + "is SIMILAR!" + newLine);

            }
            if (!diff.identical() || !diff.similar()) {
                DetailedDiff detDiff = new DetailedDiff(diff);
                List differences = detDiff.getAllDifferences();
                differencies.append("Current state on hard drive in directory:" + newLine);
                differencies.append(pathToAnalyze);
                differencies.append("is DIFFERENT!" + newLine + newLine);
                for (Object object : differences) {
                    Difference difference = (Difference) object;
                    differencies.append(difference + newLine);
                }
            }
            setProgress(i = i + 67);

            File dir2 = new File(".");
            for (File file : dir2.listFiles()) {
                if (file.getName().equals("temporary.xml")) {
                    file.delete();
                }
            }
            
            publish("XML files compared!");
            return differencies.toString();
        }

        @Override
        protected void done() {
            try {
                AnalyzeB.setEnabled(true);
                ChooseDB.setEnabled(true);
                QuitB.setEnabled(true);
                CompareB.setEnabled(true);
                compareSwingWorker = null;
                textArea.setText(get());
            } catch (ExecutionException ex) {
                Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex.getCause());
                JOptionPane.showMessageDialog(rootPane, "Exception thrown in doInBackground() of CompareSwingWorker: " + ex.getCause() + "\n");
            } catch (InterruptedException ex) {
                throw new RuntimeException("Operation interrupted", ex);
            }
        }

        @Override
        protected void process(List<String> status) {
            statusLabel.setText(status.get(0));
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MainWindow().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private java.awt.Button AnalyzeB;
    private java.awt.Button ChooseDB;
    private javax.swing.JComboBox ComboBoxXML;
    private java.awt.Button CompareB;
    private java.awt.Button QuitB;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JProgressBar progressBar;
    private java.awt.Label statusLabel;
    private javax.swing.JTextArea textArea;
    private java.awt.TextField textField;
    // End of variables declaration//GEN-END:variables
}
